package mastermind;

import java.util.Arrays;
import java.util.Collections;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Genome implements Cloneable {
    
    private Integer[] genes = new Integer[Mastermind.SEQUENCE_LENGTH];
    Random random = new Random();
    
    public Genome() {
        for (int i = 0; i < Mastermind.SEQUENCE_LENGTH; i++) {
            genes[i] = random.nextInt(Mastermind.COLORS);
        }
    }
    
    public Integer[] getGenes() {
        return genes;
    }
    
    public void setGenes(Integer[] genes) {
        this.genes = genes;
    }
    
    /**
     * Permutacja dwupunktowa dwóch genomów.
     */
    public void crossover(Genome g2) {
        Integer[] genes1 = this.getGenes();
        Integer[] genes2 = g2.getGenes();
        
        Integer[] result1 = new Integer[Mastermind.SEQUENCE_LENGTH];
        Integer[] result2 = new Integer[Mastermind.SEQUENCE_LENGTH];
        
        int crossoverPoint1 = (int) Math.floor(genes1.length * Population.CROSSOVER_POINT1);
        int crossoverPoint2 = (int) Math.floor(genes2.length * Population.CROSSOVER_POINT2);
        
        for (int i = 0; i < Mastermind.SEQUENCE_LENGTH; i++) {
            if (i < crossoverPoint1 || i > crossoverPoint2) {
                result1[i] = genes1[i];
                result2[i] = genes2[i];
            }
            else {
                result1[i] = genes2[i];
                result2[i] = genes1[i];
            }
        }
        
        this.setGenes(result1);
        g2.setGenes(result2);
    }
    
    /**
     * Mutacja genomu - zastępujemy jeden losowy kolor innym losowym kolorem.
     */
    public void mutate() {
        int i = random.nextInt(Mastermind.SEQUENCE_LENGTH);
        int j = random.nextInt(Mastermind.COLORS);
        genes[i] = j;
    }

    /**
     * Permutacja genomu - zastępujemy dwa losowe kolory pozycjami.
     */
    public void permutate() {
        int i = random.nextInt(Mastermind.SEQUENCE_LENGTH);
        int j = random.nextInt(Mastermind.SEQUENCE_LENGTH);
        
        while (i == j) {
            j = random.nextInt(Mastermind.SEQUENCE_LENGTH);
        }
        
        int tmp = genes[i];
        genes[i] = genes[j];
        genes[j] = tmp;
    }
    
    /**
     * Odwracanie genomu - losujemy geny pomiędzy dwoma losowymi punktami.
     */
    public void inverse() {
        int i = random.nextInt(Mastermind.SEQUENCE_LENGTH);
        int j = random.nextInt(Mastermind.SEQUENCE_LENGTH);
        
        while (i == j) {
            j = random.nextInt(Mastermind.SEQUENCE_LENGTH);
        }
        
        int from = Math.min(i, j);
        int to = Math.max(i, j);
        Integer[] slice = Arrays.copyOfRange(genes, from, to + 1);
        Collections.reverse(Arrays.asList(slice));
        
        for (i = 0; i <= to - from; i++) {
            genes[i + from] = slice[i];
        }
    }
    
    @Override
    public boolean equals(Object o) {
        if ( ! (o instanceof Genome)) {
            return false;
        }
          
        return Arrays.equals(genes, ((Genome) o).getGenes());
    }
    
    @Override
    public String toString() {
        String result = "";
        
        for (int gene : genes) {
            result = result.concat("(" + (gene + 1) + ")");
        }
        
        return result;
    }
    
    @Override
    public Genome clone() {
        try {
            final Genome genome = (Genome) super.clone();        
            genome.setGenes(genes);
            return genome;
        } catch (CloneNotSupportedException ex) {
            Logger.getLogger(Genome.class.getName()).log(Level.SEVERE, null, ex);
            System.exit(1);
        }
        
        return null;
    }
    
}
